﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
 using Microsoft.Xna.Framework.GamerServices;
using System.Diagnostics;

namespace Organized
{
    public class PSEmitter  : Component
    {

        struct Bullet
         {
             public Vector3 position;
             public Quaternion rotation;
             public int age;
             public int lifespan;
             public float currentsize;
             public float startSize;
             public float endSize;
             public float currentAlpha;
             public float moveSpeed;
             public Vector3 moveVector;
             public float fadeFactor;
             public float scaleFactor;
             public Vector3 moveChanger;
         }

        private struct VertexPointSprite
         {
             private Vector3 position;
             private float pointSize;
             private float pointAlpha;
 
             public VertexPointSprite(Vector3 position, float pointSize,  float pointAlpha)
             {
                 this.position = position;
                 this.pointSize = pointSize;
                 
                 this.pointAlpha = pointAlpha;
             }
 
             public static readonly VertexElement[] VertexElements =
              {
                  new VertexElement(0, 0, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Position, 0),
                  new VertexElement(0, sizeof(float)*3, VertexElementFormat.Single, VertexElementMethod.Default, VertexElementUsage.PointSize, 0),
                  new VertexElement(0, sizeof(float)*4, VertexElementFormat.Single, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 0),
              };
             public static int SizeInBytes = sizeof(float) * (3 + 1 + 1);
         }

        public Vector3 localPosOffset;
        public Vector3 myPosition;
        public Quaternion myRotation = Quaternion.Identity;
        public Vector3 myPosOffset;

        Vector3 rotationVector;
        Matrix myRotationMatrix;
        Vector3 translation;

        private int myLifeSpan;
        private int myStartSize;
        private int myEndSize;
   
        private float myRandomness;
        private Vector3 myMoveVector;
        private float myMoveSpeed;
        private Vector3 myMoveChanger;
        private float myScaleFactor;
        private float myFadeFactor;
        private int myEmitAmount;
        String myTexture;
        Matrix myOrientation;
        Effect effect;

        private bool fogEnable;
       
        double lastBulletTime = 0;
        VertexDeclaration pointSpriteVertexDeclaration;
        Texture2D bulletTexture;
        List<Bullet>  bulletList = new List<Bullet> ();

        static Random random = new Random();
        Camera cam;

        public PSEmitter(Vector3 pos, Vector3 posOffset, Quaternion rot, String type, GameScreen Parent):base(Parent){
          
            cam = Engine.Services.GetService<Camera>();
            //store position and orientation
            myPosition = pos + posOffset;
            myPosOffset = posOffset;
            myRotation = rot;

            //settings according to type
            SetType(type);
            
            effect = Engine.Content.Load<Effect>("Content/Effects/ParticleEffect");
            bulletTexture = Engine.Content.Load<Texture2D> ("Content/Textures/Particles/"+myTexture);
            pointSpriteVertexDeclaration = new VertexDeclaration(Engine.GraphicsDevice, VertexPointSprite.VertexElements);
        }

        private void SetType(String type)
        {

            switch (type)
            {
                case "DustTrail":
                    myLifeSpan = 2200;
                    myStartSize = 20; //20
                    myEndSize = 400; //400
                    myTexture = "dust";
                    myEmitAmount = 1;
                    myRandomness =  40f;
                    myMoveVector = new Vector3(0, 0.08f, -0.3f);//y=-1
                    myMoveChanger = Vector3.Zero;
                    myMoveSpeed = 1f;
                    myFadeFactor = 0.997f; //0.99
                    myScaleFactor = 1.02f;
                    break;
                case "Landmine":
                    myLifeSpan = 2500;
                    myStartSize = 30; //20
                    myEndSize = 375; //400
                    myTexture = "smoke3";
                    myEmitAmount = 1;
                    myRandomness = 60f;
                    myMoveVector = new Vector3(0, 1f, 0f);//y=-1
                    myMoveChanger = Vector3.Zero;
                    myMoveSpeed = 1.2f;
                    myFadeFactor = 0.999f; //0.99
                    myScaleFactor = 1.02f;
                    break;
                case "MuddFarts":
                    myLifeSpan = 200;
                    myStartSize = 5; //20
                    myEndSize = 60; //400
                    myTexture = "dust2";
                    myEmitAmount = 1;
                    myRandomness = 40;
                    myMoveVector = new Vector3(0, 5f, 0);//y=-1
                    myMoveSpeed = 1f;
                    myMoveChanger = new Vector3(0, -0.5f, 0); //fall down
                    myFadeFactor = 0.92f; //0.99
                    myScaleFactor = 1.02f;
                    break;

                case "FireTrail":
                    myLifeSpan = 100;
                    myStartSize = 40;
                    myEndSize = 0;
                    myTexture = "bullet";
                    myEmitAmount = 10;
                    myRandomness = 0;
                    myMoveVector = new Vector3(0,0,-1);
                    myMoveChanger = Vector3.Zero;
                    myMoveSpeed = 0.1f;
                    myFadeFactor = 0.5f;
                    myScaleFactor = 0.9f;
                    break;
            }
        }
        private void UpdateBulletPositions(float moveSpeed)
        {
            for (int i = 0; i < bulletList.Count; i++)
            {
                Bullet currentBullet = bulletList[i];
                MoveParticle(ref currentBullet.position, currentBullet.moveVector, currentBullet.moveSpeed);
               
                //alter movement
                currentBullet.moveVector += currentBullet.moveChanger;

                currentBullet.age++;
                //currentBullet.currentsize = currentBullet.startSize + ((currentBullet.age / currentBullet.lifespan) * currentBullet.endSize);
                currentBullet.currentsize *= currentBullet.scaleFactor;
                if (currentBullet.scaleFactor > 1)
                {
                    if (currentBullet.currentsize > currentBullet.endSize) { currentBullet.currentsize = currentBullet.endSize; }
                }
                else
                {
                    if (currentBullet.currentsize < currentBullet.endSize) { currentBullet.currentsize = currentBullet.endSize; }
                }
                currentBullet.currentAlpha *= currentBullet.fadeFactor;
                bulletList[i] = currentBullet;

                if (currentBullet.age >= currentBullet.lifespan || currentBullet.currentAlpha <= 0.01)
                {
                    bulletList.RemoveAt(i);
                    i--;
                }

            }
        }

        private void MoveParticle(ref Vector3 position,  Vector3 moveV, float speed)
         {
             //Vector3 addVector = Vector3.Transform(moveV, rotationQuat);
             position += moveV * speed;
             
         }

        public void EmitParticle(int waitTime, float startAlpha, float size)
        {

           // double currentTime = Engine.GameTime.TotalGameTime.TotalMilliseconds;

        
            //emit two particles
            for (int i = 0; i < myEmitAmount; i++)
            {
                Vector3 randomOffset = new Vector3(0,0,10-(float)random.NextDouble()*20);
                Vector3 positionOffset = Vector3.Transform(randomOffset, myOrientation);

                Bullet newBullet = new Bullet();
                newBullet.position = myPosition;
               // newBullet.rotation = Quaternion.CreateFromAxisAngle(new Vector3(0,1,0), (float)random.NextDouble()*360);
                newBullet.age = 0;
                newBullet.lifespan = myLifeSpan;
                newBullet.currentsize = size + ((float)random.NextDouble() * myRandomness);
                newBullet.startSize = newBullet.currentsize;
                newBullet.endSize = myEndSize;
                newBullet.currentAlpha = startAlpha;
                newBullet.moveSpeed = myMoveSpeed + ((float)random.NextDouble() / myRandomness);
                newBullet.moveVector = myMoveVector;
                newBullet.moveChanger = myMoveChanger;
                newBullet.fadeFactor = myFadeFactor - ((float)random.NextDouble()/30);
                newBullet.scaleFactor = myScaleFactor;
                bulletList.Add(newBullet);

            }

                   
                
                //lastBulletTime = currentTime;
            //}

        }

        public override void Update(){
             float moveSpeed = Engine.GameTime.ElapsedGameTime.Milliseconds / 500.0f;
             UpdateBulletPositions(moveSpeed);
        }

        public void DrawParticles()
        {
            DrawBullets();

        }
        public override void Draw()
        {     
            
            //Particles are not drawn here, but added to our gamescreens particle list
            // the draw call is made from the post processor
            //Parent.ParticleRenderList.Add(this);
            DrawBullets();
           
        }

        public void UpdateSettings(Vector3 parentPos, Matrix parentOrientation)
        {

         

     
            Vector3 positionOffset = Vector3.Transform(myPosOffset, parentOrientation);


            myPosition = parentPos +positionOffset;

        }

        Matrix worldMatrix = Matrix.Identity;

        private void DrawBullets()
        {

            if (bulletList.Count > 0)
            {
           

            Engine.GraphicsDevice.RenderState.PointSpriteEnable = true;
            Engine.GraphicsDevice.RenderState.AlphaBlendEnable = true;
            Engine.GraphicsDevice.RenderState.DepthBufferEnable = true;
            Engine.GraphicsDevice.RenderState.SourceBlend = Blend.One;
            Engine.GraphicsDevice.RenderState.DestinationBlend = Blend.One;
            Engine.GraphicsDevice.RenderState.DepthBufferWriteEnable = false;
            fogEnable = Engine.GraphicsDevice.RenderState.FogEnable;
            Engine.GraphicsDevice.RenderState.FogEnable = false;

            //Engine.GraphicsDevice.RenderState.DepthBufferEnable = false;
          
                VertexPointSprite[] spriteArray = new VertexPointSprite[bulletList.Count];
                for (int i = 0; i < bulletList.Count; i++)
                    spriteArray[i] = new VertexPointSprite(bulletList[i].position, bulletList[i].currentsize, bulletList[i].currentAlpha);

                effect.CurrentTechnique = effect.Techniques["PointSprites"];
               
                effect.Parameters["xWorld"].SetValue(worldMatrix);
                effect.Parameters["xView"].SetValue(cam.View);
                effect.Parameters["xProjection"].SetValue(cam.Projection);
                effect.Parameters["xTexture"].SetValue(bulletTexture);
               

                effect.Begin();
                foreach (EffectPass pass in effect.CurrentTechnique.Passes)
                {
                    pass.Begin();
                    Engine.GraphicsDevice.VertexDeclaration = pointSpriteVertexDeclaration;
                    Engine.GraphicsDevice.DrawUserPrimitives(PrimitiveType.PointList, spriteArray, 0, spriteArray.Length);
                    pass.End();
                }
                effect.End();

                
           
            Engine.GraphicsDevice.RenderState.DepthBufferWriteEnable = true;
            //Engine.GraphicsDevice.RenderState.DepthBufferEnable = true;
            Engine.GraphicsDevice.RenderState.PointSpriteEnable = false;
            Engine.GraphicsDevice.RenderState.AlphaBlendEnable = false;
            Engine.GraphicsDevice.RenderState.FogEnable = fogEnable;
            }
        }

    }
}
